/**
 * File: ReadInfo.java
 * Created by: mhaimel
 * Created on: Mar 5, 2010
 * CVS:  $Id: ReadInfo.java 1.0 Mar 5, 2010 2:38:57 PM mhaimel Exp $
 */
package uk.ac.ebi.curtain.model.graph.curtain;

import static uk.ac.ebi.curtain.utils.ArrayHelper.getInitalArray;
import static uk.ac.ebi.curtain.utils.ArrayHelper.incrementAndGet;
import static uk.ac.ebi.curtain.utils.ArrayHelper.toArray;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

import org.apache.commons.lang.StringUtils;

import uk.ac.ebi.curtain.utils.concurrent.CurtainAtomicIntegerArray;
import uk.ac.ebi.curtain.utils.concurrent.CurtainAtomicReferenceArray;

/**
 * @author mhaimel
 *
 */
public class ReadInfo {

	private final CurtainAtomicIntegerArray multiplicity;
	private final CurtainAtomicReferenceArray<Double> weightSum;
	private final CurtainAtomicReferenceArray<CopyOnWriteArrayList<Integer>> gapList;
	
	public ReadInfo(Integer categories) {
		this(categories,
				getInitalArray(categories, 0),
				getInitalArray(categories, 0d),
				getInitalArray(0, categories, 0));
	}
	
	@Override
	public String toString() {
		return StringUtils.join(new Object[]{
				"multiplicity="+multiplicity,
				"weightSum="+weightSum,
				"gapList="+gapList,
		},',');
	}
	
	public ReadInfo(Integer categories, Integer[] multiplicity,Double[] weightsum,Integer[][] gapList) {
		super();
		this.multiplicity = new CurtainAtomicIntegerArray(multiplicity);
		this.weightSum = new CurtainAtomicReferenceArray<Double>(weightsum);
		int len = gapList.length;
		this.gapList = new CurtainAtomicReferenceArray<CopyOnWriteArrayList<Integer>>(len);
		for(int i = 0; i < len; ++i){
			this.gapList.set(i, new CopyOnWriteArrayList<Integer>(gapList[i]));
		}
		
	}
	
	public Integer getCategories() {
		return multiplicity.length();
	}
	
	public void addRead(Integer cat, Integer gap, Double weight){
		incrementMultiplicity(cat);
		incrementWeight(cat,weight);
		addGap(cat,gap);		
	}
	
	public Integer getMultiplicity(Integer cat){
		return multiplicity.get(cat);
	}
	
	public Double getWeightSum(Integer cat){
		return weightSum.get(cat);
	}
	
	public List<Integer> getGaps(Integer cat){
		return gapList.get(cat);
	}
	
	private void addGap(Integer cat, Integer gap) {
		this.gapList.get(cat).add(gap);		
	}

	private Double incrementWeight(Integer cat, Double delta){
		return incrementAndGet(weightSum, delta, cat);
	}
	
	private Integer incrementMultiplicity(Integer cat){
		return this.multiplicity.incrementAndGet(cat);
	}
	
	public Integer[] arrayOfMultiplicity(){
		return toArray(multiplicity);
	}
	public Double[] arrayOfWeightSum(){
		return toArray(weightSum, Double.class);
	}
	public Integer[][] arrayOfGaps(){
		int len = gapList.length();
		Integer[][] arr = getInitalArray(0, len, 0);
		for(int i = 0; i < len; ++i){
			arr[i] = gapList.get(i).toArray(new Integer[0]);
		}
		return arr;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + Arrays.hashCode(arrayOfMultiplicity());
		result = prime * result + Arrays.hashCode(arrayOfWeightSum());
		result = prime * result + Arrays.hashCode(arrayOfMultiplicity());
		int cat = gapList.length();
		for(int i = 0; i < cat; ++i){
			result = prime * result + Arrays.hashCode(gapList.get(i).toArray());
		}
		return result;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		ReadInfo other = (ReadInfo) obj;
		if(!Arrays.equals(arrayOfMultiplicity(), other.arrayOfMultiplicity()))
			return false;
		if(!Arrays.equals(arrayOfWeightSum(), other.arrayOfWeightSum()))
			return false;
		int cat = gapList.length();
		for(int i = 0; i < cat; ++i){
			if(!Arrays.equals(gapList.get(i).toArray(), other.gapList.get(i).toArray()))
				return false;
		}
		return true;
	}	
}
